#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

#include "imbus_internal.h"
#include "im_hash.h"
#include "im_event_roles.h"

/************************** Declaration of internal functions ***************/
static void     __im_event_roles_initialize (IMPointer       roles);
static void     __im_event_roles_finalize   (IMPointer       roles);
static void     __im_event_roles_copy       (IMPointer       dest,
                                             IMConstPointer  src);
static IMBool   __im_event_roles_serialize  (IMConstPointer  obj,
                                             IMPointer       stream);
static IMBool   __im_event_roles_deserialize(IMPointer       obj,
                                             IMPointer       stream);
static void     __im_event_roles_class_init (IMPointer       klass);

/****************** Declaration of object and class structure ************/
struct _IMEventRoles
{
    IMObject object;

    /* private */
    IMHashTable *roles;
};

struct _IMEventRolesClass
{
    IMObjectClass parent_class;
};

/************************ Internal varable ************************/
static const IMObjectClass *__im_event_roles_parent_class = 0;

/************* Implementation of internal functions ***************/
static void
__im_event_roles_initialize (IMPointer event_roles)
{
    ((IMEventRoles*)event_roles)->roles = 0;
}

static void
__im_event_roles_finalize (IMPointer event_roles)
{
    IMEventRoles *rp = IM_EVENT_ROLES (event_roles);

    _im_assert (rp != 0);

    if (rp->roles)
        im_hash_table_destroy (rp->roles);

    rp->roles = 0;
}

static IMBool
__foreach_copy_roles (IMPointer key, IMPointer value, IMPointer table)
{
    im_hash_table_insert ((IMHashTable *)table, key, value);
    return TRUE;
}

static void
__im_event_roles_copy (IMPointer dest, IMConstPointer src)
{
    IMEventRoles       *dp = IM_EVENT_ROLES (dest);
    const IMEventRoles *sp = IM_CONST_EVENT_ROLES (src);

    _im_assert (dp && sp);

    __im_event_roles_finalize (dp);

    if (sp->roles) {
        dp->roles = im_hash_table_new (im_direct_hash, 0);
        _im_assert (dp->roles);
        im_hash_table_foreach (sp->roles, __foreach_copy_roles, dp->roles);
    }

    /* Call copy method of parent class */
    __im_event_roles_parent_class->copy (dest, src);
}

static IMBool
__foreach_get_roles (IMPointer key, IMPointer value, IMPointer buf)
{
    IMUInt32 **p = (IMUInt32**)buf;
    *(*p)++ = IM_POINTER_TO_UINT(key);
    *(*p)++ = IM_POINTER_TO_UINT(value);
    return TRUE;
}

static IMBool
__im_event_roles_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMEventRoles *rp = IM_CONST_EVENT_ROLES (obj);
    IMStream           *sp = IM_STREAM (stream);
    IMSize              num = 0;
    IMUInt32           *buf = 0;
    IMUInt32           *p   = 0;
    IMBool              ret = FALSE;

    _im_assert (rp && sp);

    /* Call parent serialize method */
    if (!__im_event_roles_parent_class->serialize (obj, stream))
        return FALSE;

    if (rp->roles && (num = im_hash_table_size (rp->roles)) != 0) {
        buf = im_new(IMUInt32, num * 2);
        p = buf;
        im_hash_table_foreach (rp->roles, __foreach_get_roles, &p);
        ret = im_stream_put_uint32_array (sp, buf, num * 2);
        im_free (buf);
    } else {
        ret = im_stream_put_uint32_array (sp, 0, 0);
    }
    return ret;
}

static IMBool
__im_event_roles_deserialize (IMPointer obj, IMPointer stream)
{
    IMEventRoles *rp = IM_EVENT_ROLES (obj);
    IMStream     *sp = IM_STREAM (stream);
    IMSize        num = 0;
    IMSize        i;
    IMUInt32     *buf = 0;

    _im_assert (rp && sp);

    /* Call parent deserialize method */
    if (!__im_event_roles_parent_class->deserialize (obj, stream))
        return FALSE;

    if (im_stream_get_data_type (sp) != IM_TYPE_UINT32_ARRAY)
        return FALSE;

    num = im_stream_get_data_size (sp);

    if (rp->roles)
        im_hash_table_remove_all (rp->roles);
    else
        rp->roles = im_hash_table_new (im_direct_hash, 0);

    if (num) {
        buf = im_new (IMUInt32, num);
        if (!im_stream_get_uint32_array (sp, buf, num)) {
            im_free (buf);
            return FALSE;
        }
        for (i = 0; i < num; i += 2)
            im_hash_table_insert (rp->roles,
                                  IM_UINT_TO_POINTER (buf[i]),
                                  IM_UINT_TO_POINTER (buf[i+1]));
        im_free (buf);
        return TRUE;
    }

    return im_stream_get_uint32_array (sp, 0, 0);
}

static void
__im_event_roles_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *)klass;

    cp->copy        = __im_event_roles_copy;
    cp->serialize   = __im_event_roles_serialize;
    cp->deserialize = __im_event_roles_deserialize;

    __im_event_roles_parent_class = im_object_class_get_parent (cp);
}

/************* Implementation of public functions ***************/
void
__im_type_register_event_roles ()
{
    static IMTypeInfo im_event_roles_type_info = 
    {
        sizeof (IMEventRolesClass),    /**< class_size >**/
        __im_event_roles_class_init,   /**< class_init >**/
        0,                             /**< class_finalize >**/

        sizeof (IMEventRoles),         /**< instance_size >**/
        __im_event_roles_initialize,   /**< instance_init >**/
        __im_event_roles_finalize,     /**< instance_finalize >**/
    };

    __im_type_register_builtin_class (IM_TYPE_OBJECT,
                                      IM_TYPE_EVENT_ROLES,
                                      "EventRoles",
                                      &im_event_roles_type_info,
                                      TRUE);
}

IMEventRoles*
im_event_roles_new ()
{
    IMEventRoles *roles = (IMEventRoles *)im_object_new (IM_TYPE_EVENT_ROLES);

    _im_assert (roles);

    return roles;
}

void
im_event_roles_clear (IMEventRoles *event_roles)
{
    _im_return_if_fail (IM_IS_EVENT_ROLES(event_roles));

    if (event_roles->roles)
        im_hash_table_remove_all (event_roles->roles);
}

void
im_event_roles_set (IMEventRoles *event_roles, IMUInt32 event_type, IMUInt roles)
{
    _im_return_if_fail (IM_IS_EVENT_ROLES(event_roles));

    if (!roles) {
        if (event_roles->roles)
            im_hash_table_remove (event_roles->roles, IM_UINT_TO_POINTER(event_type));
    } else {
        if (!event_roles->roles)
            event_roles->roles = im_hash_table_new (im_direct_hash, 0);
        im_hash_table_insert (event_roles->roles, IM_UINT_TO_POINTER(event_type), IM_UINT_TO_POINTER(roles));
    }
}

void
im_event_roles_add (IMEventRoles *event_roles, IMUInt32 event_type, IMUInt roles)
{
    _im_return_if_fail (IM_IS_EVENT_ROLES(event_roles));

    if (roles) {
        IMUInt old;

        if (!event_roles->roles)
            event_roles->roles = im_hash_table_new (im_direct_hash, 0);

        old = IM_POINTER_TO_UINT(im_hash_table_lookup (event_roles->roles, IM_UINT_TO_POINTER(event_type)));

        im_hash_table_insert (event_roles->roles, IM_UINT_TO_POINTER(event_type), IM_UINT_TO_POINTER((roles|old)));
    }
}
void
im_event_roles_remove (IMEventRoles *event_roles, IMUInt32 event_type)
{
    _im_return_if_fail (IM_IS_EVENT_ROLES(event_roles));
    if (event_roles->roles)
        im_hash_table_remove (event_roles->roles, IM_UINT_TO_POINTER(event_type));
}

IMUInt
im_event_roles_get (const IMEventRoles *event_roles, IMUInt32 event_type)
{
    _im_return_val_if_fail (IM_IS_EVENT_ROLES(event_roles), 0);

    return event_roles->roles ? IM_POINTER_TO_UINT (im_hash_table_lookup (event_roles->roles, IM_UINT_TO_POINTER(event_type))) : 0;
}

IMBool
im_event_roles_check (const IMEventRoles *event_roles, IMUInt32 event_type,
                      IMUInt role)
{
    IMUInt old;

    _im_return_val_if_fail (IM_IS_EVENT_ROLES(event_roles), 0);

    if (event_roles->roles) {
        old = IM_POINTER_TO_UINT (
               im_hash_table_lookup (event_roles->roles,
                                     IM_UINT_TO_POINTER(event_type)));
        return (old & role) == role;
    }

    return FALSE;
}

/*
vi:ts=4:nowrap:ai:expandtab
*/
